home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Graphics / Plotting / nxyplot / Source / Plot.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  64.9 KB  |  2,276 lines

  1.  
  2. /* Generated by Interface Builder */
  3.  
  4. #import "defs.h"
  5. #import "Plot.h"
  6. #import <appkit/appkit.h>
  7. #import <objc/Storage.h>
  8. #import <math.h>        /* for MAXFLOAT, etc. */
  9. #import <strings.h>
  10. #import <streams/streams.h>
  11. #import "ColumnSelectionHandler.h"
  12. #import "ErrorBarHandler.h"
  13. #import <defaults/defaults.h>
  14.  
  15. /* The following routines are in auxil.m: */
  16. extern void computeNiceLinInc(float *, float *, float *);
  17. extern void computeNiceLogInc(float *, float *, float *);
  18.  
  19. @implementation Plot
  20.  
  21. - makeSomeScrollWindows
  22. {
  23.   // the following lines exist only to initialize the ScrollWindows.....pdhowell
  24.   NXSize minsize = {0.0,0.0};
  25.   
  26.   [lineMatrixWindow   becomeScrollWindow];
  27.   [symbolMatrixWindow becomeScrollWindow];
  28.   [legendFormWindow   becomeScrollWindow];
  29.  
  30.   [lineMatrixWindow   setMinFrameSize:minsize];
  31.   [symbolMatrixWindow setMinFrameSize:minsize];
  32.   [legendFormWindow   setMinFrameSize:minsize];
  33.  
  34.   return self;
  35. }
  36.  
  37. + initialize
  38. {
  39.   const NXDefaultsVector nxyplotDefaults = {
  40.     { "colorOption", "NO"},
  41.     { "colorPrinting", "NO"},
  42.     { "cycleLineStyles", "NO"},
  43.     { "opaqueBackground", "YES"},
  44.     { NULL, NULL}
  45.   };
  46.  
  47.   self = [[Object alloc] init];
  48.   NXRegisterDefaults("nxyplot", nxyplotDefaults);
  49.   return self;
  50. }
  51.  
  52. - init
  53. {
  54.   // Initialize variables here:
  55.   nfilestotal = 0;
  56.   ncurvestotal = 0;
  57.   globaldatamin.x = MAXFLOAT;
  58.   globaldatamin.y = MAXFLOAT;
  59.   globaldatamax.x = -MAXFLOAT;
  60.   globaldatamax.y = -MAXFLOAT;
  61.   beepError = 0;
  62.   backgroundcolor = NX_COLORWHITE;
  63.   textcolor = NX_COLORBLACK;
  64.   srandom(10);            /* initialize for color selection  */
  65.   oldMin.x = 0.0;
  66.   oldMin.y = 0.0;
  67.   oldMax.x = 0.0;
  68.   oldMax.y = 0.0;
  69.   oldInc.x = 0.0;
  70.   oldInc.y = 0.0;
  71.   currentMin.x = 0.0;
  72.   currentMin.y = 0.0;
  73.   currentMax.x = 0.0;
  74.   currentMax.y = 0.0;
  75.   currentInc.x = 0.0;
  76.   currentInc.y = 0.0;
  77.  
  78.   return self;
  79. }
  80.  
  81. // Delete all data (free up the space that was malloc'ed)
  82. - removeAllFiles:sender
  83. {
  84.   int n, j;
  85.   datahunk *pdh;
  86.   const char * generictitle = "NXYPLOT";
  87.  
  88.   if (nfilestotal == 0) {
  89.     return self;
  90.   }
  91.  
  92.   // Put up an alert panel.  This method is called by a menu item and also
  93.   // by the removeAndOpen method; only if it's called by the menu item
  94.   // do we want to put up the alert panel.
  95.   if (sender != self) {
  96.     if (NXRunAlertPanel("Remove all", "Remove all files and clear plot",
  97.             "OK", "Cancel", NULL) == NX_ALERTALTERNATE) {
  98.       return self;
  99.     }
  100.   }
  101.  
  102.   for (n=nfilestotal-1; n>=0; n--) {
  103.     pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  104.     for (j = 0; j < pdh->ncurves; j++) {
  105.       free( (void *)*(pdh->y+j) );
  106.     }
  107.     free( (void *)(pdh->y) );
  108.     if (pdh->has_eybars) {
  109.       for (j = 0; j < pdh->ncurves; j++) {
  110.         free( (void *)*(pdh->ey+j) );
  111.       }
  112.       free( (void *)(pdh->ey));
  113.     }
  114.     free( (void *)(pdh->x) );
  115.     if (pdh->has_exbars) {
  116.       free( (void *)(pdh->ex));
  117.     }
  118.     free( (void *)(pdh->filename) );
  119.   }
  120.   [datahunkArray empty];
  121.   [self adjustPanels:ncurvestotal :-1]; /* -1 is a special signal */
  122.   nfilestotal = 0;
  123.  
  124.   [columnSelectionHandler removeAll:self];
  125.  
  126.   [errorBarHandler removeAll:self];
  127.  
  128.   [canvas display];        /* clear the canvas */
  129.  
  130.   [[canvas window] setTitle:generictitle];
  131.  
  132.   ncurvestotal = 0;
  133.  
  134.   // reset globaldatamin/max
  135.   globaldatamin.x = MAXFLOAT;
  136.   globaldatamin.y = MAXFLOAT;
  137.   globaldatamax.x = -MAXFLOAT;
  138.   globaldatamax.y = -MAXFLOAT;
  139.  
  140.   // clear xMin/Max/Inc and yMin/Max/Inc windows:
  141.   [xMin setStringValue:"" at:0];
  142.   [xMax setStringValue:"" at:0];
  143.   [xInc setStringValue:"" at:0];
  144.   [yMin setStringValue:"" at:0];
  145.   [yMax setStringValue:"" at:0];
  146.   [yInc setStringValue:"" at:0];
  147.   srandom(10);            /* initialize for color selection  */
  148.  
  149.   oldMin.x = 0.0;
  150.   oldMin.y = 0.0;
  151.   oldMax.x = 0.0;
  152.   oldMax.y = 0.0;
  153.   oldInc.x = 0.0;
  154.   oldInc.y = 0.0;
  155.   currentMin.x = 0.0;
  156.   currentMin.y = 0.0;
  157.   currentMax.x = 0.0;
  158.   currentMax.y = 0.0;
  159.   currentInc.x = 0.0;
  160.   currentInc.y = 0.0;
  161.  
  162.   return self;
  163. }
  164.  
  165. // Remove all existing files and open a new one
  166. - removeAndOpen:sender
  167. {
  168.   if (NXRunAlertPanel("New",
  169.               "Remove all files, clear plot\nand open new file",
  170.               "OK", "Cancel", NULL) == NX_ALERTALTERNATE) {
  171.     return self;
  172.   }
  173.  
  174.   [self removeAllFiles:self];
  175.   [self open:self];
  176.   return self;
  177. }
  178.  
  179.  
  180. - fixFileRemovalPanel:sender
  181. {
  182.   int n;
  183.   char title[80];
  184.   NXCoord dy;
  185.   int numrows, numcols;
  186.   NXSize cellsize, intercell;
  187.  
  188.   // Fix up the filename matrix
  189.   [fileRemovalMatrix getNumRows:&numrows numCols:&numcols];
  190.   [fileRemovalMatrix getCellSize:&cellsize];
  191.   [fileRemovalMatrix getIntercell:&intercell];
  192.  
  193.   [fileRemovalMatrix renewRows:nfilestotal cols:1];
  194.   dy = (NXCoord)(nfilestotal - numrows) * (cellsize.height + intercell.height);
  195.   for (n=0; n<nfilestotal; n++) {
  196.     if (!strncmp([self filename:(unsigned)n], "pasteboard", 10))
  197.       sprintf(title, "pasteboard");
  198.     else
  199.       sprintf(title, strrchr([self filename:(unsigned)n], '/') + 1);
  200.     [[fileRemovalMatrix cellAt:n :0] setStringValue:title];
  201.   }
  202.   [fileRemovalMatrix sizeToCells];
  203.   [fileRemovalMatrix moveBy:0.0 :-dy];
  204.  
  205.   // Fix up the buttons that go with the matrix of names
  206.   [fileRemovalButtons getCellSize:&cellsize];
  207.   [fileRemovalButtons getIntercell:&intercell];
  208.   dy = (NXCoord)(nfilestotal - numrows) * (cellsize.height + intercell.height);
  209.   [fileRemovalButtons renewRows:nfilestotal cols:1];
  210.   [fileRemovalButtons sizeToCells];
  211.   [fileRemovalButtons moveBy:0.0 :-dy];
  212.   for (n=0; n<nfilestotal; n++) {
  213.     [ [fileRemovalButtons cellAt:n :0] setState:0]; /* a safety play */
  214.   }
  215.  
  216.   [fileRemovalPanel display];
  217.  
  218.   if (sender != self) {
  219.     /* If this method is called from the menu, make the window key. */
  220.     [fileRemovalPanel makeKeyAndOrderFront:self];
  221.   }
  222.  
  223.   return self;
  224. }
  225.  
  226.  
  227. // Remove some existing files; the file removal is not hard, but correctly
  228. // updating the linestyle, symbolstyle, and legend matrices is harder.
  229. // Correctly handling the ColumnSelection and ErrorBar panels is even harder.
  230. - removeSomeFiles:sender
  231. {
  232.   int n, i, j;
  233.   int old_index = 0, current_index = 0;
  234.   /* These integers point to the columns of the linestyle and symbolstyle
  235.    * matrices as we run along updating the matrices.  They also serve in
  236.    * updating the curvecolors array.
  237.    */
  238.   datahunk *pdh;
  239.   int *newlinestyles, *newsymbolstyles;
  240.  
  241.   if (nfilestotal == 0) {
  242.     return self;
  243.   }
  244.   /*
  245.    * If all files are marked for deletion, we will call the "removeAllFiles"
  246.    * method.  So check here to see if all files are marked for deletion.
  247.    */
  248.   i = 1;
  249.   for (n=0; n < nfilestotal; n++) {
  250.     if ([[fileRemovalButtons cellAt:n :0] state] == 0) {
  251.       i = 0;            // there is a file not being deleted
  252.       break;
  253.     }
  254.   }
  255.   if (i == 1) {            // all files are to be deleted
  256.     [self removeAllFiles:self];
  257.     return self;
  258.   }
  259.  
  260.  
  261.   /* Figure out what the linestyle and symbolstyle matrices should look
  262.    * like after the file removal process.  We do it this way because simply
  263.    * copying the columns of the matrices fails after the renewRows:cols:
  264.    * message is sent to them.
  265.    */
  266.   newlinestyles = (int *)malloc(ncurvestotal * sizeof(int));
  267.   newsymbolstyles = (int *)malloc(ncurvestotal * sizeof(int));
  268.   for (n=0; n < nfilestotal; n++) {
  269.     if ([[fileRemovalButtons cellAt:n :0] state] == 1) {
  270.       /* Just bump the index if the file is to be deleted.  */
  271.       old_index += [self nCurves:n];
  272.     }
  273.     else {
  274.       for (i=0; i < [self nCurves:n]; i++) {
  275.     for (j=0; j < N_LINE_STYLES; j++) {
  276.       if ([ [lineMatrix cellAt:j :old_index] state] == 1) {
  277.         newlinestyles[current_index] = j;
  278.         break;
  279.       }
  280.     }
  281.     for (j=0; j < N_SYMBOL_STYLES; j++) {
  282.       if ([ [symbolMatrix cellAt:j :old_index] state] == 1) {
  283.         newsymbolstyles[current_index] = j;
  284.         break;
  285.       }
  286.     }
  287.     /* Update the curvecolors array: */
  288.     curvecolors[current_index] = curvecolors[old_index];
  289.     old_index++;
  290.     current_index++;
  291.       }
  292.     }
  293.   }
  294.   /* We could do a realloc on curvecolors here, but not much would be saved. */
  295.  
  296.   /* Reset the running indices. */
  297.   old_index = 0;
  298.   current_index = 0;
  299.  
  300.   for (n=0; n < nfilestotal; n++) {
  301.     /* Is the nth file marked for deletion?  */
  302.     if ([[fileRemovalButtons cellAt:n :0] state] == 1) {    /* yes it is */
  303.       pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  304.       for (j = 0; j < pdh->ncurves; j++) {
  305.     free( (void *)*(pdh->y+j) );
  306.       }
  307.       free( (void *)(pdh->y) );
  308.       if (pdh->has_eybars) {
  309.     for (j = 0; j < pdh->ncurves; j++) {
  310.       free( (void *)*(pdh->ey+j) );
  311.     }
  312.     free( (void *)(pdh->ey));
  313.       }
  314.       free( (void *)(pdh->x) );
  315.       if (pdh->has_exbars) {
  316.     free( (void *)(pdh->ex));
  317.       }
  318.       free( (void *)(pdh->filename) );
  319.       old_index += [self nCurves:n];
  320.     }
  321.     else {
  322.       /* Column copying of the linestyle and symbolstyle matrices
  323.        * is handled later.  Do the legend form here.
  324.        */
  325.       for (j=0; j < [self nCurves:n]; j++) {
  326.     [legendForm setStringValue:[legendForm stringValueAt:old_index]
  327.                     at:current_index];
  328.     [legendForm drawCellAt:current_index];
  329.     old_index++;
  330.     current_index++;
  331.       }
  332.     }
  333.   }
  334.  
  335.   /* Get rid of extraneous legendForm entries: */
  336.   for (j=current_index; j<ncurvestotal; j++) {
  337.     [legendForm removeEntryAt:j];
  338.   }
  339.   [legendForm sizeToFit];
  340.   [ [legendForm window] display];
  341.  
  342.   /*
  343.    * Do the ColumnSelectionHandler manipulation here, before nfilestotal
  344.    * gets reset.
  345.    */
  346.   [columnSelectionHandler update:self];
  347.   [errorBarHandler update:self];
  348.  
  349.   /* We put the datahunkArray manipulation here because the removeAt method
  350.    * shifts the elements of the datahunkArray to close the gap created by
  351.    * removing one element; for this reason we count down rather than up.
  352.    */
  353.   j = nfilestotal;
  354.   for (n=nfilestotal-1; n >= 0; n--) {
  355.     if ([[fileRemovalButtons cellAt:n :0] state] == 1) {
  356.       [datahunkArray removeElementAt:(unsigned)n];
  357.       j--;
  358.     }
  359.   }
  360.   nfilestotal = j;
  361.   ncurvestotal = current_index;
  362.  
  363.   /* Now resize and display the linestyle and symbolstyle matrices */
  364.   [lineMatrix renewRows:N_LINE_STYLES cols:ncurvestotal];
  365.   [lineMatrix sizeToCells];
  366.   for (i=0; i<ncurvestotal; i++) {
  367.     for (j=0; j<N_LINE_STYLES; j++) {
  368.       [ [lineMatrix cellAt:j :i] setState:0];
  369.     }
  370.     [ [lineMatrix cellAt:newlinestyles[i] :i] setState:1];
  371.   }
  372.  
  373.   [lineText renewRows:1 cols:ncurvestotal];
  374.   [ [lineMatrix window] display];
  375.  
  376.   [symbolMatrix renewRows:N_SYMBOL_STYLES cols:ncurvestotal];
  377.   [symbolMatrix sizeToCells];
  378.   for (i=0; i<ncurvestotal; i++) {
  379.     for (j=0; j<N_SYMBOL_STYLES; j++) {
  380.       [ [symbolMatrix cellAt:j :i] setState:0];
  381.     }
  382.     [ [symbolMatrix cellAt:newsymbolstyles[i] :i] setState:1];
  383.   }
  384.   [symbolText renewRows:1 cols:ncurvestotal];
  385.   [ [symbolMatrix window] display];
  386.  
  387.   [self adjustScrollWindows];
  388.  
  389.   /* must also fix up appearence of the file removal window */
  390.   [self fixFileRemovalPanel:self];
  391.   [fileRemovalPanel performClose:self];
  392.   free((void *)newlinestyles);
  393.   free((void *)newsymbolstyles);
  394.  
  395.   /* and replot automatically */
  396.   if (nfilestotal == 0) {
  397.     [canvas display];
  398.   }
  399.   else {
  400.     [self drawPlot:self];
  401.   }
  402.  
  403.   return self;
  404. }
  405.  
  406.  
  407. /* return the x data for the nth file */
  408. - (NXCoord *)xdata:(int)n
  409. {
  410.   datahunk *pdh;
  411.  
  412.   pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  413.   return pdh->x;
  414. }
  415.  
  416. /* return the y data for the nth file */
  417. - (NXCoord **)ydata:(int)n
  418. {
  419.   datahunk *pdh;
  420.  
  421.   pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  422.   return pdh->y;
  423. }
  424.  
  425. /* return the y error data for the nth file */
  426. - (NXCoord **)eydata:(int)n
  427. {
  428.   datahunk *pdh;
  429.  
  430.   pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  431.   return pdh->ey;
  432. }
  433.  
  434. /* return the x error data for the nth file */
  435. - (NXCoord *)exdata:(int)n
  436. {
  437.   datahunk *pdh;
  438.  
  439.   pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  440.   return pdh->ex;
  441. }
  442.  
  443. /* return the number of x-points in the nth file */
  444. - (int)nPoints:(int)n
  445. {
  446.   datahunk *pdh;
  447.  
  448.   pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  449.   return pdh->npoints;
  450. }
  451.  
  452. /* return the number of curves in the nth file */
  453. - (int)nCurves:(int)n
  454. {
  455.   datahunk *pdh;
  456.  
  457.   pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  458.   return pdh->ncurves;
  459. }
  460.  
  461. /* return the name of the nth file */
  462. - (char *)filename:(unsigned)n
  463. {
  464.   datahunk *pdh;
  465.  
  466.   pdh = (datahunk *)[datahunkArray elementAt:n];
  467.   return pdh->filename;
  468. }
  469.  
  470. /* Does the nth file have error bars in y? */
  471. - (BOOL) has_eybars:(int)n
  472. {
  473.   datahunk *pdh;
  474.  
  475.   pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  476.   return pdh->has_eybars;
  477. }
  478.  
  479. /* Does the nth file have error bars in x? */
  480. - (BOOL) has_exbars:(int)n
  481. {
  482.   datahunk *pdh;
  483.  
  484.   pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  485.   return pdh->has_exbars;
  486. }
  487.  
  488. - (int)nCurvesTotal     { return ncurvestotal;}
  489.  
  490. - (int)nFiles           { return nfilestotal;}
  491.  
  492. - makeLineStyle:(int)aCurve :(int)lineStyle
  493. {
  494.   int   row;
  495.  
  496.   for (row = 0; row < N_LINE_STYLES; row++)
  497.     [[lineMatrix cellAt:row :aCurve] setState:0]; /* turn off all */
  498.   [[lineMatrix cellAt:lineStyle :aCurve] setState:1];
  499.   return self;
  500. }
  501.  
  502. - makeSymbolType:(int)aCurve :(int)symType;
  503. {
  504.   int   row;
  505.  
  506.   for (row = 0; row < N_SYMBOL_STYLES; row++)
  507.     [[symbolMatrix cellAt:row :aCurve] setState:0]; /* turn off all */
  508.   [[symbolMatrix cellAt:symType :aCurve] setState:1];
  509.   return self;
  510. }
  511.  
  512. - (BOOL) xaxisLog
  513. {
  514.   if ( [xLinLog state] ) return YES;
  515.   else return NO;
  516. }
  517.  
  518. - forceXaxisLinear
  519. {
  520.   [xLinLog setState:0];
  521.   [xLinLog display];
  522.   return self;
  523. }
  524.  
  525. - forceXaxisLog
  526. {
  527.   [xLinLog setState:1];
  528.   [xLinLog display];
  529.   return self;
  530. }
  531.  
  532. - (BOOL) yaxisLog
  533. {
  534.   if ( [yLinLog state] ) return YES;
  535.   else return NO;
  536. }
  537.  
  538. - forceYaxisLinear
  539. {
  540.   [yLinLog setState:0];
  541.   [yLinLog display];
  542.   return self;
  543. }
  544.  
  545. - forceYaxisLog
  546. {
  547.   [yLinLog setState:1];
  548.   [yLinLog display];
  549.   return self;
  550. }
  551.  
  552. - (BOOL) shouldChangeLegendFont
  553. {
  554.   if ( [changeLegendFont state] ) return YES;
  555.   else return NO;
  556. }
  557.  
  558. - (BOOL) shouldChangeLegendTitleFont
  559. {
  560.   if ( [changeLegendTitleFont state] ) return YES;
  561.   else return NO;
  562. }
  563.  
  564. - (BOOL) shouldChangeMainTitleFont
  565. {
  566.   if ( [changeMainTitleFont state] ) return YES;
  567.   else return NO;
  568. }
  569.  
  570. - (BOOL) shouldChangeYTitleFont
  571. {
  572.   if ( [changeYTitleFont state] ) return YES;
  573.   else return NO;
  574. }
  575.  
  576. - (BOOL) shouldChangeXTitleFont
  577. {
  578.   if ( [changeXTitleFont state] ) return YES;
  579.   else return NO;
  580. }
  581.  
  582. - (BOOL) shouldChangeTicLabelFont
  583. {
  584.   if ( [changeTicLabelFont state] ) return YES;
  585.   else return NO;
  586. }
  587.  
  588. - (int)providelinestyle:(int)aCurve
  589. {
  590.   int   row, cellstate;
  591.  
  592.   /* First, if line is turned off, return that */
  593.   if ([ [lineMatrix cellAt:N_LINE_STYLES-1 :aCurve] state] == 1) {
  594.     return N_LINE_STYLES-1;
  595.   }
  596.   /*
  597.    * Next, check if we are printing or previewing and if we should
  598.    * cycle the line styles
  599.    */
  600.   if ( ( ([printPreview state] == 1) || (NXDrawingStatus == NX_PRINTING))
  601.       && ([accPrintLineStyleButton state] == 1) ) {
  602.     return aCurve % (N_LINE_STYLES - 1);
  603.   }
  604.   /*
  605.    * Here we can just look at the linestyle matrix.
  606.    */
  607.   else {
  608.     for (row = 0; row < N_LINE_STYLES; row++) {
  609.       cellstate = [ [lineMatrix cellAt:row :aCurve] state];
  610.       if (cellstate == 1) return row;
  611.     }
  612.   }
  613.   return 0;            /* for safety */
  614. }
  615.  
  616. - (int)providesymbolstyle:(int)aCurve
  617. {
  618.   int   row, cellstate;
  619.  
  620.   for (row = 0; row < N_SYMBOL_STYLES; row++) {
  621.     cellstate = [ [symbolMatrix cellAt:row :aCurve] state];
  622.     if (cellstate == 1) return row;
  623.   }
  624.   return 0;            /* for safety */
  625. }
  626.  
  627. /*
  628.  * Changed floats to doubles in the following group.  With floats, when
  629.  * the value was 250.4, the value returned by floatValueAt: was
  630.  * 250.399994 (for example).  (This appears to be a problem with the
  631.  * atof function on Unix 32-bit systems.)  This gave troubles in the
  632.  * tic mark routine in PlotView.m.  
  633.  */
  634. - (double)provideXmin  {return [xMin doubleValueAt:0];}
  635. - (double)provideXmax  {return [xMax doubleValueAt:0];}
  636. - (double)provideXinc  {return [xInc doubleValueAt:0];}
  637. - (double)provideYmin  {return [yMin doubleValueAt:0];}
  638. - (double)provideYmax  {return [yMax doubleValueAt:0];}
  639. - (double)provideYinc  {return [yInc doubleValueAt:0];}
  640.  
  641. - resetXmin:(double)aNum { [xMin setDoubleValue:aNum at:0]; return self; }
  642. - resetXmax:(double)aNum { [xMax setDoubleValue:aNum at:0]; return self; }
  643. - resetXinc:(double)aNum { [xInc setDoubleValue:aNum at:0]; return self; }
  644. - resetYmin:(double)aNum { [yMin setDoubleValue:aNum at:0]; return self; }
  645. - resetYmax:(double)aNum { [yMax setDoubleValue:aNum at:0]; return self; }
  646. - resetYinc:(double)aNum { [yInc setDoubleValue:aNum at:0]; return self; }
  647.  
  648. - (float)provideGlobalXmin {return globaldatamin.x;}
  649. - (float)provideGlobalYmin {return globaldatamin.y;}
  650.  
  651. - resetMinMax:sender
  652. {
  653.   int n;
  654.   datahunk * pdh;
  655.  
  656.   // We have to go through all the data and recalculate min/max since
  657.   // some data curves may have been "turned off" (by setting their linestyle
  658.   // to none and symbolstyle to none).
  659.  
  660.   for (n=0; n<nfilestotal; n++) {
  661.     pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  662.     [self findMinMax:pdh];
  663.   }
  664.   [self findGlobalMinMax];
  665.  
  666.   [self niceMinMaxInc];
  667.   [self drawPlot:self];        /* redraw plot automatically */
  668.   return self;
  669. }
  670.  
  671. - drawPlotButton:(int)state
  672. {
  673.   if (state==0) {
  674.     [plotButton highlight:NO];    /* will display normal title */
  675.   }
  676.   if (state==1) {
  677.     [plotButton highlight:YES];    /* will display alternate title */
  678.   }
  679.   return self;
  680. }
  681.  
  682. // We make the plotParam object responsible for checking parameters
  683. // before the PlotView object is called.  Thus the PlotView object can
  684. // assume the parameters are OK, and it doesn't have to do any checking.
  685. // Things to check: xinc has the same sign as xmax-xmin (same for y);
  686. // no negative data if log plot requested on x or y axis; there won't be
  687. // too many tic marks requested.
  688. - sanityCheck
  689. {
  690.   float xinc = [self provideXinc];
  691.   float xmax = [self provideXmax], xmin = [self provideXmin];
  692.   float yinc = [self provideYinc];
  693.   float ymax = [self provideYmax], ymin = [self provideYmin];
  694.   int   nticmarks;
  695.  
  696.   /* First check: no nonpositive data if logarithmic axis */
  697.   /* Also check that increment is > 5 (need rint(log10(increment)) >=1 ) */
  698.   if ( [self xaxisLog] ) {
  699.     if (globaldatamin.x <= 0.0 || xmin <= 0.0 || xmax <= 0.0) {
  700.       [xLinLog setState:0];    /* back to linear */
  701.       NXBeep();            /* audible alert */
  702.       beepError = 1;
  703.     }
  704.     if (xinc < 5.0) {
  705.       [self resetXinc:(double)10.0];
  706.       NXBeep();
  707.       beepError = 12;
  708.     }
  709.   }
  710.   if ( [self yaxisLog] ) {
  711.     if (globaldatamin.y <= 0.0 || ymin <= 0.0 || ymax <= 0.0) {
  712.       [yLinLog setState:0];    /* back to linear */
  713.       NXBeep();            /* audible alert */
  714.       beepError = 2;
  715.     }
  716.     if (yinc < 5.0) {
  717.       [self resetYinc:(double)10.0];
  718.       NXBeep();
  719.       beepError = 12;
  720.     }
  721.   }
  722.   /* Second check: xinc has same sign as xmax and xmin */
  723.   if (xinc*(xmax-xmin) <= 0.0) { /* the bad case - avoid infinite loop */
  724.     if (xinc < 0.0) {        /*     in PlotView:drawSelf           */
  725.       xinc = -xinc;
  726.       [self resetXinc:xinc];
  727.       NXBeep();
  728.       beepError = 3;
  729.     }
  730.     if (xmax <= xmin) {
  731.       [self niceMinMaxInc];
  732.       NXBeep();            /* alert */
  733.       beepError = 4;
  734.     }
  735.   }
  736.   /* And similarly for yinc */
  737.   if (yinc*(ymax-ymin) <= 0.0) { /* the bad case - avoid infinite loop */
  738.     if (yinc < 0.0) {
  739.       yinc = -yinc;
  740.       [self resetYinc:yinc];
  741.       NXBeep();            /* alert */
  742.       beepError = 5;
  743.     }
  744.     if (ymax <= ymin) {
  745.       [self niceMinMaxInc];
  746.       NXBeep();            /* alert */
  747.       beepError = 6;
  748.     }
  749.   }
  750.   /* Third check: no more than 100 (say) tic marks on either axis */
  751.   if ( ![self xaxisLog] ) {    /*  linear axis */
  752.     nticmarks = (int) ((xmax - xmin) / xinc) ;
  753.     if (nticmarks > 100) {
  754.       computeNiceLinInc(&xmin, &xmax, &xinc);
  755.       [self resetXmin:xmin];
  756.       [self resetXmax:xmax];
  757.       [self resetXinc:xinc];
  758.       NXBeep();            /* alert */
  759.       beepError = 7;
  760.     }
  761.   }
  762.   if ( ![self yaxisLog] ) {    /*  linear axis */
  763.     nticmarks = (int) ((ymax - ymin) / yinc) ;
  764.     if (nticmarks > 100) {
  765.       computeNiceLinInc(&ymin, &ymax, &yinc);
  766.       [self resetYmin:ymin];
  767.       [self resetYmax:ymax];
  768.       [self resetYinc:yinc];
  769.       NXBeep();            /* alert */
  770.       beepError = 8;
  771.     }
  772.   }
  773.  
  774.   return self;
  775. }
  776.  
  777. - drawPlot:sender
  778. {
  779.   char c_xmin[20], c_xmax[20], c_ymin[20], c_ymax[20], c_xinc[20], c_yinc[20];
  780.   float xmin, xmax, ymin, ymax, xinc, yinc;
  781.  
  782.   if (nfilestotal==0) return self;
  783.  
  784.   [self drawPlotButton:1];    /* display "Plotting" */
  785.   [self sanityCheck];        /* disallow various bad parameters */
  786.  
  787.   /* maybe save min/max/inc */
  788.   /* This really gets crufty: when we look at the TextField xMin and
  789.    * get its float value via [xMin floatValueAt:0], we get
  790.    * a float which is the result of applying atof() to a string.  The
  791.    * float which results may not be the same as the float that was originally
  792.    * written into the TextField.  So we apply the process ourselves:
  793.    * float --> string (via sprintf) --> float (via atof).  Ugly, but necessary
  794.    * (otherwise, if you zoom, then hit the Plot button twice, then hit
  795.    * the Previous View button, you might not get back to where you want).
  796.    */
  797.   sprintf(c_xmin,"%g",currentMin.x);
  798.   sprintf(c_xmax,"%g",currentMax.x);
  799.   sprintf(c_xinc,"%g",currentInc.x);
  800.   sprintf(c_ymin,"%g",currentMin.y);
  801.   sprintf(c_ymax,"%g",currentMax.y);
  802.   sprintf(c_yinc,"%g",currentInc.y);
  803.   xmin = (float)atof(c_xmin);
  804.   xmax = (float)atof(c_xmax);
  805.   xinc = (float)atof(c_xinc);
  806.   ymin = (float)atof(c_ymin);
  807.   ymax = (float)atof(c_ymax);
  808.   yinc = (float)atof(c_yinc);
  809.   if ( [xMin floatValueAt:0] != xmin
  810.       || [xMax floatValueAt:0] != xmax
  811.       || [xInc floatValueAt:0] != xinc
  812.       || [yMin floatValueAt:0] != ymin
  813.       || [yMax floatValueAt:0] != ymax
  814.       || [yInc floatValueAt:0] != yinc ) {
  815.     oldMin.x = currentMin.x;
  816.     currentMin.x = [xMin floatValueAt:0];
  817.     oldMax.x = currentMax.x;
  818.     currentMax.x = [xMax floatValueAt:0];
  819.     oldInc.x = currentInc.x;
  820.     currentInc.x = [xInc floatValueAt:0];
  821.     oldMin.y = currentMin.y;
  822.     currentMin.y = [yMin floatValueAt:0];
  823.     oldMax.y = currentMax.y;
  824.     currentMax.y = [yMax floatValueAt:0];
  825.     oldInc.y = currentInc.y;
  826.     currentInc.y = [yInc floatValueAt:0];
  827.   }
  828.  
  829.   [canvas display];
  830.   [self drawPlotButton:0];    /* display "Plot" */
  831.   return self;
  832. }
  833.  
  834. // Allocate enough memory and read the data points
  835. /*
  836.  * This code makes the following assumptions:
  837.  * 1. Any data on a line following the character "!" is to be discarded.
  838.  * 2. We can determine the number of curves by looking at the first
  839.  * line of data, which should be of the form
  840.  *  x  y1  y2    ...    yn
  841.  * (possibly separated by commas, with possible trailing comment).
  842.  * 3. Other lines of the file may contain arbitrary text, but contain no
  843.  * numerals or periods (these get interpreted as floating point numbers
  844.  * when the file is scanned); also, anything after a "!" is discarded.
  845.  *
  846.  * It is not easy to make a completely general and bullet-proof scanning
  847.  * routine.  This code is fairly robust and was easy to write.
  848.  */
  849. - (int) readData:(NXStream *)aDataStream :(char *)fname
  850. {
  851.   BOOL    inword = NO;
  852.   char    c;
  853.   int     j, size = ALLOCSIZE;
  854.   int     tmpint = 0;        /* tmpint initialized to avoid compiler warning */
  855.   int     oldncurves = ncurvestotal;
  856.   datahunk *pdh = (void *)NULL;
  857.   BOOL    noxdata = NO;
  858.   float   tmpfloat = 0.0;    /* initialized to avoid compiler warning */
  859.   
  860.   [self preludeToReading:fname :&pdh];    /* take care of some housekeeping */
  861.   
  862.   /* Figure out the number of curves in the file by reading characters  */
  863.   /* until a newline is encountered; the number of curves is one less   */
  864.   /* than the number of words found.                                    */
  865.   /* We assume the input file is an ascii file; a compressed file will  */
  866.   /* have been pumped through zcat and written to a temporary file.     */
  867.   pdh->ncurves = -1;
  868. top_of_file: ;
  869.   while (1) {
  870.     c = (char)NXGetc(aDataStream);
  871.     if (c == (char)EOF) {
  872.       break;            /* EOF, break out of while loop */
  873.     }
  874.     if (c == '\n') {
  875.       if (pdh->ncurves == -1) {
  876.     goto top_of_file;    // ugh, but there may be a blank line
  877.       }
  878.       else {
  879.     break;            /* we have found some data, break from while loop */
  880.       }
  881.     }
  882.     if (c == '!') {        /* comment signal: start discarding characters */
  883.       while ( (c=(char)NXGetc(aDataStream)) != '\n' ) ; /*  do nothing */
  884.       if (pdh->ncurves == -1)    /* any data found yet? */
  885.     goto top_of_file;        /* ugh */
  886.       else
  887.     break;
  888.     }
  889.     else if ((inword==NO) && !(c==' ' || c=='\t')) {
  890.       pdh->ncurves++;
  891.       inword = YES;
  892.     }
  893.     else if ((inword==YES) && (c==' ' || c=='\t')) {
  894.       inword = NO;
  895.     }
  896.   }
  897.   if (pdh->ncurves == -1) {    /* couldn't find "\n", give up (after cleanup) */
  898.     [plotButton setAltTitle:"Plotting"]; /* reset plot button */
  899.     [plotButton highlight:NO];
  900.     NXPing();                       /* force redraw */
  901.     free( (void *)(pdh->filename) );
  902.     nfilestotal--;
  903.     return 0;
  904.   }
  905.   if (pdh->ncurves == 0) {    /* only one column, assume x data are integers */
  906.     noxdata = YES;
  907.     tmpfloat = 1.0;
  908.     pdh->ncurves = 1;
  909.     if (pdh->has_exbars || pdh->has_eybars) {
  910.       NXRunAlertPanel("Read Data (with error bars)",
  911.               "Error bars expected, only one curve found\n"
  912.               "Unsetting error bar button and continuing",
  913.               "OK", NULL, NULL);
  914.       [errorBars setTitle:"No error bars"];
  915.       pdh->has_exbars = NO;
  916.       pdh->has_eybars = NO;
  917.     }
  918.   }
  919.  
  920.   /*
  921.    * We read more than one column; if there are error bars we must adjust ncurves.
  922.    *    case            pdh->ncurves        true no. of curves
  923.    *   y only            2n                    n
  924.    *   x only            n (>=2)               n-1
  925.    *   y and x          2n+1 (>=3)             n
  926.    */
  927.   if (pdh->has_eybars && !pdh->has_exbars) {
  928.     if ( pdh->ncurves % 2  !=  0 ) {
  929.       NXRunAlertPanel("Read Data (with error bars)",
  930.               "Strange number of curves found\n"
  931.               "Unsetting error bar button and continuing",
  932.               "OK", NULL, NULL);
  933.       [errorBars setTitle:"No error bars"];
  934.       pdh->has_eybars = NO;
  935.     }
  936.     else {
  937.       pdh->ncurves = pdh->ncurves / 2;
  938.     }
  939.   }
  940.   else if (pdh->has_exbars && !pdh->has_eybars) {
  941.     if ( pdh->ncurves < 2 ) {
  942.       NXRunAlertPanel("Read Data (with error bars)",
  943.               "Too few curves found\n"
  944.               "Unsetting error bar button and continuing",
  945.               "OK", NULL, NULL);
  946.       [errorBars setTitle:"No error bars"];
  947.       pdh->has_exbars = NO;
  948.     }
  949.     else {
  950.       pdh->ncurves--;
  951.     }
  952.   }
  953.   else if (pdh->has_exbars && pdh->has_eybars) {
  954.     if ( pdh->ncurves < 3  ||  pdh->ncurves % 2 == 0 ) {
  955.       NXRunAlertPanel("Read Data (with error bars)",
  956.               "Bad number of curves found\n"
  957.               "Unsetting error bar button and continuing",
  958.               "OK", NULL, NULL);
  959.       [errorBars setTitle:"No error bars"];
  960.       pdh->has_exbars = NO;
  961.       pdh->has_eybars = NO;
  962.     }
  963.     else {
  964.       pdh->ncurves = (pdh->ncurves - 1) / 2;
  965.     }
  966.   }
  967.  
  968.   /* Now read the data into memory */
  969.   NXSeek(aDataStream, 0L, NX_FROMSTART);
  970.     
  971.   pdh->x = (NXCoord *)malloc( size * sizeof(NXCoord) );
  972.   if (pdh->has_exbars) {
  973.     pdh->ex = (NXCoord *)malloc( size * sizeof(NXCoord) );
  974.   }
  975.   pdh->y = (NXCoord **)malloc( pdh->ncurves * sizeof(NXCoord *) );
  976.   for (j = 0; j < pdh->ncurves; j++) {
  977.     *(pdh->y+j) = (NXCoord *)malloc( size * sizeof(NXCoord) );
  978.   }
  979.   if (pdh->has_eybars) {
  980.     pdh->ey = (NXCoord **)malloc( pdh->ncurves * sizeof(NXCoord *) );
  981.     for (j = 0; j < pdh->ncurves; j++) {
  982.       *(pdh->ey+j) = (NXCoord *)malloc( size * sizeof(NXCoord) );
  983.     }
  984.   }
  985.   pdh->npoints = 0;
  986.   while(1) {
  987.     if (noxdata) {
  988.       *(pdh->x+pdh->npoints) = tmpfloat++;
  989.     }
  990.     else {
  991.       if (pdh->has_exbars) {    /* x error bars, read two items*/
  992.     while ( (tmpint=NXScanf(aDataStream, "%f", pdh->x+pdh->npoints)) == 0 ) {
  993.       if (c == '!') {
  994.         while ( (c=(char)NXGetc(aDataStream)) != '\n' ) ; /* do nothing */
  995.         goto skipline;    /* ugh */
  996.       }
  997.     }
  998.     while ( (tmpint=NXScanf(aDataStream, "%f", pdh->ex+pdh->npoints)) == 0 ) {
  999.       if (c == '!') {
  1000.         while ( (c=(char)NXGetc(aDataStream)) != '\n' ) ; /* do nothing */
  1001.         goto skipline;    /* ugh */
  1002.       }
  1003.     }
  1004.       }
  1005.       else {            /* no x error bars, read one item */
  1006.     while ( (tmpint=NXScanf(aDataStream, "%f", pdh->x+pdh->npoints)) == 0 ) {
  1007.       c = (char)NXGetc(aDataStream);    /* throw away extraneous characters */
  1008.       if (c == '!') {
  1009.         while ( (c=(char)NXGetc(aDataStream)) != '\n' ) ; /* do nothing */
  1010.         goto skipline;    /* ugh */
  1011.       }
  1012.     }
  1013.       }
  1014.       if (tmpint == EOF) break;    /* break out of the while(1) loop */
  1015.     }
  1016.  
  1017.     if (pdh->has_eybars) {    /* y error bars, read two items */
  1018.       for (j = 0; j < 2 * pdh->ncurves; j++) {
  1019.     if (j%2 == 0) {        /* j is even, reading y value */
  1020.       while( (tmpint=NXScanf(aDataStream, "%f", *(pdh->y+(j/2))+ pdh->npoints)) == 0 ) {
  1021.         c = (char)NXGetc(aDataStream);    /* throw away the next character */
  1022.         if (c == '!') {        /* comment signal; start discarding characters */
  1023.           while ( (c=(char)NXGetc(aDataStream)) != '\n' ) ; /* do nothing */
  1024.           goto skipline;    /* ugh */
  1025.         }
  1026.       }
  1027.     }
  1028.     else {            /* j is odd, reading error */
  1029.       while( (tmpint=NXScanf(aDataStream, "%f", *(pdh->ey+(j-1)/2)+ pdh->npoints)) == 0 ) {
  1030.         c = (char)NXGetc(aDataStream);
  1031.         if (c == '!') {
  1032.           while ( (c=(char)NXGetc(aDataStream)) != '\n' ) ;
  1033.           goto skipline;
  1034.         }
  1035.       }
  1036.     }
  1037.       }
  1038.     }
  1039.     else {            /* no error bars, read one item */
  1040.       for (j = 0; j < pdh->ncurves; j++) {
  1041. /*
  1042.  * Try to allow extraneous characters here (if scanf returns 0, which means
  1043.  * it didn't find a number, just do a getc on the input stream to throw that
  1044.  * character away.  This will allow commas and alphabetic characters in the
  1045.  * middle of an input line (no digits or periods, though!).
  1046.  */
  1047.     while( (tmpint=NXScanf(aDataStream, "%f", *(pdh->y+j)+ pdh->npoints)) == 0 ) {
  1048.       c = (char)NXGetc(aDataStream);    /* throw away the next character */
  1049.       if (c == '!') {        /* comment signal; start discarding characters */
  1050.         while ( (c=(char)NXGetc(aDataStream)) != '\n' ) ; /* do nothing */
  1051.         goto skipline;    /* ugh */
  1052.       }
  1053.     }
  1054.       }
  1055.     }
  1056.     if (tmpint == EOF) break;    /* could get this if noxdata==YES */
  1057.     pdh->npoints++;
  1058.     if (pdh->npoints == size) {        /* get more memory */
  1059.       size += ALLOCSIZE;
  1060.       pdh->x = (NXCoord *)realloc(pdh->x, size * sizeof(NXCoord));
  1061.       if (pdh->has_exbars) {
  1062.     pdh->ex = (NXCoord *) realloc(pdh->ex, size * sizeof(NXCoord));
  1063.       }
  1064.       for (j = 0; j < pdh->ncurves; j++) {
  1065.     *(pdh->y+j) = (NXCoord *)realloc( *(pdh->y+j), size * sizeof(NXCoord) );
  1066.       }
  1067.       if (pdh->has_eybars) {
  1068.     for (j = 0; j < pdh->ncurves; j++) {
  1069.       *(pdh->ey+j) = (NXCoord *)realloc( *(pdh->ey+j), size * sizeof(NXCoord) );
  1070.     }
  1071.       }
  1072.     }
  1073. skipline: ;
  1074.   }
  1075.  
  1076.   [self postludeToReading:fname :oldncurves :pdh];
  1077.  
  1078.   ncurvestotal += pdh->ncurves;
  1079.  
  1080.   return pdh->npoints;
  1081. }
  1082.  
  1083. /* Might want to make sure INLINE_MATH is defined when math.h is included
  1084.  * (for guaranteed fast logarithms?)
  1085.  */
  1086. - checkLinLog:(datahunk *)pdh
  1087. {
  1088.   /* Check x and y axes -- do a heuristic test for log/lin.
  1089.    * The test employed comes from M. Merriam and xyplot.
  1090.    */
  1091.   double    scale, linsum, logsum;
  1092.   register  double tmp;
  1093.   int       i, j;
  1094.  
  1095.   /* First test x axis.  */
  1096.   if (pdh->datamax.x > 0.0  &&  pdh->datamin.x > 0.0
  1097.       && pdh->datamax.x != pdh->datamin.x) {
  1098.     scale = fabs( (double)pdh->datamax.x - (double)pdh->datamin.x );
  1099.     linsum = 0.0;
  1100.     for (j=1; j<pdh->npoints; j++) {
  1101.       tmp = ( (double)pdh->x[j]-(double)pdh->x[j-1] )/(double)scale;
  1102.       linsum += tmp*tmp;
  1103.     }
  1104.     scale = log10( (double)pdh->datamax.x/(double)pdh->datamin.x );
  1105.     /* what if datamax.x<datamin.x? */
  1106.     logsum = 0.0;
  1107.     for (i=1; i<pdh->npoints; i++) {
  1108.       tmp = log10( (double)pdh->x[i]/(double)pdh->x[i-1] ) / scale;
  1109.       logsum += tmp*tmp;
  1110.     }
  1111.     if (linsum < logsum) {
  1112.       pdh->xaxislin = YES;    /* linear axis */
  1113.     }
  1114.     else {
  1115.       pdh->xaxislin = NO;    /* logarithmic axis */
  1116.     }
  1117.   }
  1118.   else {
  1119.     pdh->xaxislin = YES;    /* linear */
  1120.   }
  1121.   /* Now test y axis */
  1122.   if (pdh->datamax.y > 0.0  &&  pdh->datamin.y > 0.0
  1123.       && pdh->datamax.y != pdh->datamin.y) {
  1124.     scale = fabs( (double)pdh->datamax.y - (double)pdh->datamin.y );
  1125.     linsum = 0.0;
  1126.     for (j=0; j<pdh->ncurves; j++) {
  1127.       for (i=1; i<pdh->npoints; i++) {
  1128.     tmp = ( (double)*(*(pdh->y+j)+i) - (double)*(*(pdh->y+j)+i-1) )
  1129.       / scale;        /* avoid overflow */
  1130.     linsum += tmp*tmp;
  1131.       }
  1132.     }
  1133.     scale = log10((double)pdh->datamax.y/(double)pdh->datamin.y);
  1134.     logsum = 0.0;
  1135.     for (j=0; j<pdh->ncurves; j++) {
  1136.       for (i=1; i<pdh->npoints;i++) {
  1137.     tmp = log10( (double)*(*(pdh->y+j)+i)/(double)*(*(pdh->y+j)+i-1) ) / scale;
  1138.     logsum += tmp*tmp;
  1139.       }
  1140.     }
  1141.     if (linsum < logsum) {
  1142.       pdh->yaxislin = YES;    /* linear axis */
  1143.     }
  1144.     else {
  1145.       pdh->yaxislin = NO;    /* logarithmic axis */
  1146.     }
  1147.   }
  1148.   else {
  1149.     pdh->yaxislin = YES;    /* linear */
  1150.   }
  1151.   return self;
  1152. }
  1153.  
  1154. // This routine works as follows:
  1155. // If these is just one file, we set the x and y axes to linear or logarithmic
  1156. // depending on our heuristic.
  1157. // If there is more than one file, we don't change the axes unless there
  1158. // would be an illegal result (trying to plot a negative number on a
  1159. // logarithmic axis).
  1160. - checkGlobalLinLog
  1161. {
  1162.   datahunk *pdh;
  1163.  
  1164.   if (nfilestotal == 1) {
  1165.     pdh = (datahunk *)[datahunkArray elementAt:0];
  1166.     if ( pdh->xaxislin )
  1167.       [xLinLog setState:0]; /* linear */
  1168.     else
  1169.       [xLinLog setState:1]; /* logarithmic */ 
  1170.     if ( pdh->yaxislin )
  1171.       [yLinLog setState:0]; /* linear */
  1172.     else
  1173.       [yLinLog setState:1]; /* logarithmic */
  1174.   }
  1175.   else {
  1176.     if ( [self xaxisLog] && globaldatamin.x <= 0.0) {
  1177.       [xLinLog setState:0];    /* back to linear */
  1178.       NXBeep();            /* audible alert */
  1179.       beepError = 9;
  1180.     }
  1181.     if ( [self yaxisLog] && globaldatamin.y <= 0.0) {
  1182.       [yLinLog setState:0];    /* back to linear */
  1183.       NXBeep();            /* audible alert */
  1184.       beepError = 10;
  1185.     }
  1186.   }
  1187.   [xLinLog display];
  1188.   [yLinLog display];
  1189.  
  1190.   return self;
  1191. }
  1192.  
  1193. - adjustLineStyleMatrix:(int)column :(int)row
  1194. {
  1195.   /* Adjust the given column of the lineMatrix object, turning off all
  1196.    * buttons except for the given row.
  1197.    */
  1198.   int i;
  1199.  
  1200.   for (i = 0; i < N_LINE_STYLES; i++) {
  1201.     [ [lineMatrix cellAt:i :column] setState:0];
  1202.   }
  1203.   [ [lineMatrix cellAt:row :column] setState:1];  
  1204.  
  1205.   return self;
  1206. }
  1207.  
  1208. - redisplayLineStyleMatrix
  1209. {
  1210.   [lineMatrix display];
  1211.   return self;
  1212. }
  1213.  
  1214. - adjustSymbolTypeMatrix:(int)column :(int)row
  1215. {
  1216.   /* Adjust the given column of the symbolMatrix object, turning off all
  1217.    * buttons except for the given row.
  1218.    */
  1219.   int i;
  1220.  
  1221.   for (i = 0; i < N_SYMBOL_STYLES; i++) {
  1222.     [ [symbolMatrix cellAt:i :column] setState:0];
  1223.   }
  1224.   [ [symbolMatrix cellAt:row :column] setState:1];  
  1225.   return self;
  1226. }
  1227.  
  1228. - redisplaySymbolTypeMatrix
  1229. {
  1230.   [symbolMatrix display];
  1231.   return self;
  1232. }
  1233.  
  1234. - adjustPanels:(int)oldn :(int)newn
  1235. {
  1236.   /*
  1237.    * Resize the linestyle matrix, the symbolstyle matrix, and
  1238.    * the legend form.
  1239.    * Also arrange to select and highlight only the top button in
  1240.    * each column of the linestyle and symbolstyle matrices.
  1241.    * This code could stand to be cleaned up.
  1242.    */
  1243.   char formtitle[80];
  1244.   int j, k;
  1245.   datahunk *pdh;
  1246.  
  1247.   if (newn == -1) {        /* flag to delete all and start over */
  1248.     for (j=oldn-1; j>=1; j--) {
  1249.       [lineText removeColAt:j andFree:YES];
  1250.       [symbolText removeColAt:j andFree:YES];
  1251.       [lineMatrix removeColAt:j andFree:YES];
  1252.       [symbolMatrix removeColAt:j andFree:YES];
  1253.       [legendForm removeEntryAt:j];
  1254.     }
  1255.     for (j=0; j<1; j++) {
  1256.       if ( [ [lineMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
  1257.     [ [lineMatrix cellAt:0 :j] setState:1];
  1258.       if ( [ [symbolMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
  1259.     [ [symbolMatrix cellAt:0 :j] setState:1];
  1260.       for (k=1; k<N_LINE_STYLES; k++) {
  1261.     [ [lineMatrix cellAt:k :j] setState:0];
  1262.       }
  1263.       for (k=1; k<N_SYMBOL_STYLES; k++) {
  1264.     [ [symbolMatrix cellAt:k :j] setState:0];
  1265.       }
  1266.       sprintf(formtitle, "Curve %d", j+1);
  1267.       [legendForm setStringValue:formtitle at:j];
  1268.       [legendForm drawCellAt:j];
  1269.       sprintf(formtitle, "%d", j+1);
  1270.       [[lineText cellAt:0 :j] setStringValue:formtitle]; /* titles on columns */
  1271.       [[symbolText cellAt:0 :j] setStringValue:formtitle]; /* titles on columns */
  1272.     }
  1273.     //    [legendTitle setStringValue:"Legend" at:0];  should we uncomment this?
  1274.     //                                          (would need to add an outlet in IB)
  1275.     [lineText sizeToCells];
  1276.     [symbolText sizeToCells];
  1277.     [lineMatrix sizeToCells];
  1278.     [symbolMatrix sizeToCells];
  1279.     [legendForm sizeToFit];
  1280.     [ [lineMatrix window] display];   /* redraw the whole window so extra */
  1281.     [ [symbolMatrix window] display]; /* columns get erased               */
  1282.     [ [legendForm window] display];
  1283.     [curveNumber setIntValue:1]; /* update curve no. on color panel */
  1284.     [curveColorWell setColor:NX_COLORBLACK]; /* and update the color */
  1285.     /* (the background and text color wells are not reset) */
  1286.   }
  1287.  
  1288.   if ( (oldn == 0) && (newn > 1) ) { /* can only happen first time */
  1289.     pdh = [datahunkArray elementAt:0];
  1290.     for (j=1; j<newn; j++) {
  1291.       [lineText addCol];
  1292.       [symbolText addCol];
  1293.       [lineMatrix addCol];
  1294.       if ( [ [lineMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
  1295.     [ [lineMatrix cellAt:0 :j] setState:1];
  1296.       /* highlight 1st row new column -- this occurs automatically */
  1297.       [symbolMatrix addCol];
  1298.       if ( [ [symbolMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
  1299.     [ [symbolMatrix cellAt:0 :j] setState:1];
  1300.       /* highlighting of 1st row new column occurs automatically */
  1301.       sprintf(formtitle, "Curve %d:", j+1);
  1302.       [legendForm addEntry:formtitle];
  1303.       sprintf(formtitle, "%s, Curve %d",
  1304.           strrchr(pdh->filename,'/')==NULL ? pdh->filename
  1305.                                            : strrchr(pdh->filename,'/') + 1,
  1306.           j+1);
  1307.       [legendForm setStringValue:formtitle at:j];
  1308.       [legendForm drawCellAt:j];
  1309.       sprintf(formtitle, "%d", j+1);
  1310.       [[lineText cellAt:0 :j] setStringValue:formtitle]; /* titles on columns */
  1311.       [[symbolText cellAt:0 :j] setStringValue:formtitle]; /* titles on columns */
  1312.     }
  1313.     sprintf(formtitle, "%s, Curve 1",
  1314.         strrchr(pdh->filename,'/')==NULL ? pdh->filename
  1315.                                          : strrchr(pdh->filename,'/') + 1);
  1316.     [legendForm setStringValue:formtitle at:0];
  1317.     [legendForm drawCellAt:0];
  1318.     [lineText sizeToCells];
  1319.     [symbolText sizeToCells];
  1320.     [lineMatrix sizeToCells];
  1321.     [symbolMatrix sizeToCells];
  1322.     [legendForm sizeToFit];
  1323.     [lineText display];
  1324.     [symbolText display];
  1325.     [lineMatrix display];    /* don't need to redraw the window here */
  1326.     [symbolMatrix display];
  1327.     [legendForm display];
  1328.   }
  1329.   if ( (oldn == 0) && (newn == 1) ) { /* only for legendForm update */
  1330.     pdh = [datahunkArray elementAt:0];
  1331.     sprintf(formtitle, "%s, Curve 1",
  1332.         strrchr(pdh->filename,'/')==NULL ? pdh->filename
  1333.                                          : strrchr(pdh->filename,'/') + 1);
  1334.     [legendForm setStringValue:formtitle at:0];
  1335.     [legendForm drawCellAt:0];
  1336.     [legendForm display];
  1337.   }
  1338.   if ( oldn != 0 ) {        /* must add columns */
  1339.     pdh = [datahunkArray elementAt:(nfilestotal-1)];
  1340.     for (j=oldn; j<oldn+newn; j++) {
  1341.       [lineText addCol];
  1342.       [symbolText addCol];
  1343.       [lineMatrix addCol];
  1344.       [ [lineMatrix cellAt:0 :j] setState:1];
  1345.       /* highlighting of 1st row new column occurs automatically */
  1346.       [symbolMatrix addCol];
  1347.       [ [symbolMatrix cellAt:0 :j] setState:1];
  1348.       /* highlighting of 1st row new column occurs automatically */
  1349.       sprintf(formtitle, "Curve %d:", j+1);
  1350.       [legendForm addEntry:formtitle];
  1351.       sprintf(formtitle, "%s, Curve %d",
  1352.           strrchr(pdh->filename,'/')==NULL ? pdh->filename
  1353.                                            : strrchr(pdh->filename,'/') + 1,
  1354.           j+1-oldn);
  1355.       [legendForm setStringValue:formtitle at:j];
  1356.       [legendForm drawCellAt:j];
  1357.       sprintf(formtitle, "%d", j+1);
  1358.       [[lineText cellAt:0 :j] setStringValue:formtitle];
  1359.       [[symbolText cellAt:0 :j] setStringValue:formtitle];
  1360.     }
  1361.     [lineText sizeToCells];
  1362.     [symbolText sizeToCells];
  1363.     [lineMatrix sizeToCells];
  1364.     [symbolMatrix sizeToCells];
  1365.     [legendForm sizeToFit];
  1366.     [lineText display];
  1367.     [symbolText display];
  1368.     [lineMatrix display];    /* don't need to redraw the window here */
  1369.     [symbolMatrix display];
  1370.     [legendForm display];
  1371.   }
  1372.  
  1373.   [self adjustScrollWindows];
  1374.   return self;
  1375. }
  1376.  
  1377. - adjustScrollWindows
  1378. {
  1379. // code from pdhowell for ScrollWindowing
  1380.   NXRect legendFormFrame, lineMatrixFrame, symbolMatrixFrame;
  1381.  
  1382.   [legendForm getFrame:&legendFormFrame];
  1383.   [lineMatrix getFrame:&lineMatrixFrame];
  1384.   [symbolMatrix getFrame:&symbolMatrixFrame];
  1385. // Put MAX in the the following lines because we don't want to allow the windows
  1386. // to get so small that the sliders aren't drawn; the 282 and 288 are the original
  1387. // sizes of the lineMatrixWindow and symbolMatrixWindow -- dcj
  1388.   [[[lineMatrixWindow contentView] docView]
  1389.                                    sizeTo:MAX(128.0+lineMatrixFrame.size.width,282.0)
  1390.                                          :92+lineMatrixFrame.size.height];
  1391.   [[[symbolMatrixWindow contentView] docView]
  1392.                                   sizeTo:MAX(128.0+symbolMatrixFrame.size.width,288.0)
  1393.                                         :92+symbolMatrixFrame.size.height];
  1394.   [[[legendFormWindow contentView] docView]
  1395.                                    sizeTo:32+legendFormFrame.size.width
  1396.                                          :192+legendFormFrame.size.height];
  1397.  
  1398. // Try sending windowDidResize (the "self" is bogus but does no harm) -- dcj
  1399.   [legendFormWindow windowDidResize:self];
  1400.   [lineMatrixWindow windowDidResize:self];
  1401.   [symbolMatrixWindow windowDidResize:self];
  1402.  
  1403. // Try to get the windows to display properly ... dcj
  1404.   [ [lineMatrix window] display];
  1405.   [ [symbolMatrix window] display];
  1406.   [ [legendForm window] display];
  1407.  
  1408.   return self;
  1409. }
  1410.  
  1411. // Go through a particular datahunk and find values for datamin.x,
  1412. // datamax.x, datamin.y, datamax.y
  1413. // We ignore any curves that are "turned off" (linestyle=NONE & symbolstyle=NONE);
  1414. // if all curves in the datahunk are turned off we ignore the x-data, too.
  1415. - findMinMax:(datahunk *)pdh
  1416. {
  1417.   int i, j;
  1418.   datahunk *qdh;
  1419.   int n, ncurve;
  1420.   BOOL ignore;
  1421.  
  1422.   pdh->datamin.x = MAXFLOAT;
  1423.   pdh->datamax.x = -MAXFLOAT;
  1424.   pdh->datamin.y = MAXFLOAT;
  1425.   pdh->datamax.y = -MAXFLOAT;
  1426.  
  1427.   // Find out which curves belong to datahunk pdh
  1428.   ncurve = 0;
  1429.   for (n=0; n<nfilestotal; n++) {
  1430.     qdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  1431.     if (qdh == pdh)
  1432.       break;
  1433.     else
  1434.       ncurve += qdh->ncurves;
  1435.   }
  1436.   // Now we know to look at curves ncurve, ncurve+1,...,ncurve+pdh->ncurves-1
  1437.   ignore = YES;
  1438.   for (n = ncurve; n < ncurve + pdh->ncurves; n++) {
  1439.     if ([self providelinestyle:n] != NOLINE
  1440.     || [self providesymbolstyle:n] != NOSYMBOL)
  1441.       ignore = NO;
  1442.   }
  1443.   // Now it's possible we want to ignore all the curves.
  1444.   if (ignore) return self;
  1445.  
  1446.   // If we don't ignore all the curves then we must look at the x data.
  1447.   for (i = 0; i < pdh->npoints; i++)  {
  1448.     pdh->datamin.x = MIN(pdh->datamin.x, pdh->x[i]);
  1449.     pdh->datamax.x = MAX(pdh->datamax.x, pdh->x[i]);
  1450.   }
  1451.   for (j = 0; j < pdh->ncurves; j++) {
  1452.     if ([self providelinestyle:(ncurve+j)] != NOLINE
  1453.     || [self providesymbolstyle:(ncurve+j)] != NOSYMBOL) {
  1454.       for (i = 0; i < pdh->npoints; i++) {
  1455.     pdh->datamin.y = MIN(pdh->datamin.y, *(*(pdh->y+j)+i));
  1456.     pdh->datamax.y = MAX(pdh->datamax.y, *(*(pdh->y+j)+i));
  1457.       }
  1458.     }
  1459.   }
  1460.   return self;
  1461. }
  1462.  
  1463. - findGlobalMinMax
  1464. {
  1465.   int n;
  1466.   datahunk *pdh;
  1467.  
  1468.   globaldatamin.x = MAXFLOAT;
  1469.   globaldatamin.y = MAXFLOAT;
  1470.   globaldatamax.x = -MAXFLOAT;
  1471.   globaldatamax.y = -MAXFLOAT;
  1472.   for (n=0; n<nfilestotal; n++) {
  1473.     pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  1474.     globaldatamin.x = MIN(globaldatamin.x, pdh->datamin.x);
  1475.     globaldatamax.x = MAX(globaldatamax.x, pdh->datamax.x);
  1476.     globaldatamin.y = MIN(globaldatamin.y, pdh->datamin.y);
  1477.     globaldatamax.y = MAX(globaldatamax.y, pdh->datamax.y);
  1478.   }
  1479.   return self;
  1480. }
  1481.  
  1482. // Get pleasing values for the min, max, and increments
  1483. - niceMinMaxInc
  1484. {
  1485.   float fmin, fmax, finc;
  1486.  
  1487.   fmin = globaldatamin.x;
  1488.   fmax = globaldatamax.x;
  1489.   if ([self xaxisLog] ) {
  1490.     computeNiceLogInc(&fmin, &fmax, &finc);
  1491.   }
  1492.   else {
  1493.     computeNiceLinInc(&fmin, &fmax, &finc);
  1494.   }
  1495.   [self resetXmin:fmin];
  1496.   [self resetXmax:fmax];
  1497.   [self resetXinc:finc];
  1498.  
  1499.   fmin = globaldatamin.y;
  1500.   fmax = globaldatamax.y;
  1501.   if ([self yaxisLog] ) {
  1502.     computeNiceLogInc(&fmin, &fmax, &finc);
  1503.   }
  1504.   else {
  1505.     computeNiceLinInc(&fmin, &fmax, &finc);
  1506.   }
  1507.   [self resetYmin:fmin];
  1508.   [self resetYmax:fmax];
  1509.   [self resetYinc:finc];
  1510.   return self;
  1511. }
  1512.  
  1513. // Use the OpenPanel object to get a filename
  1514. - open:sender
  1515. {
  1516.   static const char *const fileTypes[2] = {NULL, NULL};
  1517.                 /* this is supposedly all ASCII files */
  1518.   char  fname[256];
  1519.   char  tempfname[256], command[512];
  1520.   id openPanel = [[OpenPanel new] allowMultipleFiles:NO];
  1521.  
  1522.   [openPanel setAccessoryView:nil]; /* may have to clean out an accessory view */
  1523.   if (nfilestotal == 0) {
  1524.     if (strncmp([errorBars title], "No error bars", 13) != 0) {
  1525.       [openPanel setTitle:"Open (error bars)"];
  1526.     }
  1527.     else {
  1528.       [openPanel setTitle:"Open"];        /* make sure title is OK (cf. binary open) */
  1529.     }
  1530.   }
  1531.   else {
  1532.     if (strncmp([errorBars title], "No error bars", 13) != 0) {
  1533.       [openPanel setTitle:"Another (error bars)"]; /* 21 character limit here? */
  1534.     }
  1535.     else {
  1536.       [openPanel setTitle:"Open Additional File"];
  1537.     }
  1538.   }
  1539.  
  1540.   if ([openPanel runModalForTypes:fileTypes])  {
  1541.     strncpy(fname, (char *)[openPanel filename], 256);
  1542.     // Check to see if we are trying to open a compressed file:
  1543.     if (fname[strlen(fname)-1]=='Z' && fname[strlen(fname)-2]=='.') {
  1544.       // set plot button title:
  1545.       [plotButton setAltTitle:"Uncompressing"];
  1546.       [plotButton highlight:YES];
  1547.       NXPing();            /* force plotButton redraw */
  1548.       // Uncompress the file into a temporary file:
  1549.       strcpy(tempfname, "/tmp/file000000.xyp");
  1550.       NXGetTempFilename(tempfname, 9);
  1551.       sprintf(command, "zcat %s > %s\n", fname, tempfname);
  1552.       system(command);        /* no error checking */
  1553.       // Now just go ahead and open the temporary file:
  1554.       [self openFile:tempfname :fname];
  1555.       // After returning from the openFile it is safe to unlink:
  1556.       unlink(tempfname);
  1557.     }
  1558.     else {
  1559.       [self openFile:fname :fname];
  1560.     }
  1561.   }
  1562.   return self;
  1563. }
  1564.  
  1565. - openFile:(char *)dataFile :(char *)realName
  1566. {
  1567.   NXStream *dataStream;
  1568.  
  1569.   if ((dataStream = NXMapFile(dataFile, NX_READONLY)) == NULL)  {
  1570.     NXRunAlertPanel("Open", "Cannot open %s", "OK", NULL, NULL, dataFile);
  1571.     return self;
  1572.   }
  1573.  
  1574.   if ([self readData:dataStream :realName] == 0)  {
  1575.     NXRunAlertPanel("Read", "Couldn't read any data from %s", "OK",
  1576.             NULL, NULL, dataFile);
  1577.     NXCloseMemory(dataStream, NX_FREEBUFFER);
  1578.     return self;
  1579.   }
  1580.   NXCloseMemory(dataStream, NX_FREEBUFFER);
  1581.   [self plotPrepAndDraw];
  1582.   return self;
  1583. }
  1584.  
  1585. - openBinary:sender
  1586. {
  1587.   static const char *const fileTypes[2] = {NULL, NULL};
  1588.   char  fname[256];
  1589.   id openPanel = [[OpenPanel new] allowMultipleFiles:NO];
  1590.  
  1591.   [openPanel setAccessoryView:binaryOpenAccessory];
  1592.   if (strncmp([errorBars title], "No error bars", 13) != 0) {
  1593.     [openPanel setTitle:"Open Binary (error bars)"];
  1594.   }
  1595.   else {
  1596.     [openPanel setTitle:"Open Binary File"];
  1597.   }
  1598.  
  1599.   if ([openPanel runModalForTypes:fileTypes])  {
  1600.     if ([binaryOpenForm intValueAt:0] < 1) {
  1601.       NXRunAlertPanel("Binary Read",
  1602.               "Number of curves is less than 1\n"
  1603.               "Be sure to set this correctly",
  1604.               "OK", NULL, NULL);
  1605.       return self;
  1606.     }
  1607.     strncpy(fname, (char *)[openPanel filename], 256);
  1608.     [self openBinaryFile:fname];
  1609.   }
  1610.   return self;
  1611. }
  1612.  
  1613. #import <sys/stat.h>
  1614. - openBinaryFile:(char *)dataFile
  1615. {
  1616.   struct stat filestat;
  1617.   int    filesize, numpoints;
  1618.   int    numcurves = [binaryOpenForm intValueAt:0];
  1619.   BOOL   xdatathere = [binaryXdatathere state];
  1620.   NXStream *dataStream;
  1621.   int    numcols;        /* number of "columns" expected in the data file */
  1622.  
  1623.   if (strncmp([errorBars title], "No error bars", 13) == 0) {
  1624.     numcols = numcurves;    /* no error bars */
  1625.   }
  1626.   else if (strncmp([errorBars title], "y only", 6) == 0) {
  1627.     numcols = 2*numcurves;
  1628.   }
  1629.   else if (strncmp([errorBars title], "x only", 6) == 0) {
  1630.     numcols = numcurves + 1;
  1631.   }
  1632.   else {
  1633.     numcols = 2*numcurves + 1;
  1634.   }
  1635.  
  1636.   stat(dataFile, &filestat);
  1637.   filesize = filestat.st_size;
  1638.  
  1639.   if (xdatathere) {
  1640.     // This consistency check is not sufficient to guarantee numcurves is
  1641.     // correct, but it's better than nothing.
  1642.     if ( (filesize % (sizeof(NXCoord)*(numcols+1))) != 0 ) {
  1643.       NXRunAlertPanel("Binary Read",
  1644.               "File size inconsistent with number\n"
  1645.               "of curves specified",
  1646.               "OK", NULL, NULL);
  1647.       return self;
  1648.     }
  1649.     numpoints = filesize/(sizeof(NXCoord)*(numcols+1));
  1650.   }
  1651.   else {
  1652.     if ( (filesize % (sizeof(NXCoord)*numcols)) != 0 ) {
  1653.       NXRunAlertPanel("Binary Read",
  1654.               "File size inconsistent with number\n"
  1655.               "of curves specified",
  1656.               "OK", NULL, NULL);
  1657.       return self;
  1658.     }
  1659.     numpoints = filesize/(sizeof(NXCoord)*(numcols));
  1660.   }
  1661.   
  1662.   if ((dataStream = NXMapFile(dataFile, NX_READONLY)) == NULL)  {
  1663.     NXRunAlertPanel("Open", "Cannot open %s", "OK", NULL, NULL, dataFile);
  1664.     return self;
  1665.   }
  1666.  
  1667.   if ([self readBinaryData:dataStream :dataFile :numcurves 
  1668.                           :numpoints :xdatathere] == 0)  {
  1669.     NXRunAlertPanel("Read", "Couldn't read any data from %s", "OK",
  1670.             NULL, NULL, dataFile);
  1671.     NXCloseMemory(dataStream, NX_FREEBUFFER);
  1672.     return self;
  1673.   }
  1674.   NXCloseMemory(dataStream, NX_FREEBUFFER);
  1675.   [self plotPrepAndDraw];
  1676.  
  1677.   return self;
  1678. }
  1679.  
  1680. - (int)readBinaryData:(NXStream *)aDataStream
  1681.                      :(char *)fname
  1682.                      :(int)numcurves
  1683.                      :(int)numpoints
  1684.                      :(BOOL)xdatathere
  1685. {
  1686.   int      j, oldncurves = ncurvestotal;
  1687.   datahunk *pdh = (void *)NULL;
  1688.   
  1689.   [self preludeToReading:fname :&pdh];    /* take care of some housekeeping */
  1690.   pdh->ncurves = numcurves;
  1691.  
  1692.   /* Now read the data into memory */
  1693.   NXSeek(aDataStream, 0L, NX_FROMSTART);
  1694.     
  1695.   pdh->x = (NXCoord *)malloc( numpoints * sizeof(NXCoord) );
  1696.   if (pdh->has_exbars) {
  1697.     pdh->ex = (NXCoord *)malloc( numpoints * sizeof(NXCoord *) );
  1698.   }
  1699.   pdh->y = (NXCoord **)malloc( pdh->ncurves * sizeof(NXCoord *) );
  1700.   for (j = 0; j < pdh->ncurves; j++) {
  1701.     *(pdh->y+j) = (NXCoord *)malloc( numpoints * sizeof(NXCoord) );
  1702.   }
  1703.   if (pdh->has_eybars) {
  1704.     pdh->ey = (NXCoord **)malloc( pdh->ncurves * sizeof(NXCoord *) );
  1705.     for (j = 0; j < pdh->ncurves; j++) {
  1706.       *(pdh->ey+j) = (NXCoord *)malloc( numpoints * sizeof(NXCoord) );
  1707.     }
  1708.   }
  1709.     
  1710.   pdh->npoints = numpoints;
  1711.  
  1712.   if (xdatathere) {
  1713.     NXRead(aDataStream, pdh->x, numpoints*sizeof(NXCoord));
  1714.   }
  1715.   else {
  1716.     for (j=0; j < numpoints; j++) {
  1717.       pdh->x[j] = (float)j;
  1718.     }
  1719.   }
  1720.   if (pdh->has_exbars) {
  1721.     NXRead(aDataStream, pdh->ex, numpoints*sizeof(NXCoord));
  1722.   }
  1723.   for (j = 0; j < pdh->ncurves; j++) {
  1724.     NXRead(aDataStream, *(pdh->y+j), numpoints*sizeof(NXCoord));
  1725.     if (pdh->has_eybars) {
  1726.       NXRead(aDataStream, *(pdh->ey+j), numpoints*sizeof(NXCoord));
  1727.     }
  1728.   }
  1729.  
  1730.   [self postludeToReading:fname :oldncurves :pdh];
  1731.  
  1732.   ncurvestotal += pdh->ncurves;
  1733.  
  1734.   return pdh->npoints;
  1735. }
  1736.  
  1737.  
  1738. - postludeToReading:(char *)fname :(int)oldncurves :(datahunk *)pdh
  1739. {
  1740.   int   j;
  1741.   float hue = 0.0;
  1742. #define N_PREDEFINED_COLORS 9
  1743.   NXColor predefined_colors[] = {NX_COLORRED, NX_COLORGREEN, NX_COLORBLUE,
  1744.                      NX_COLORCYAN, NX_COLORYELLOW, NX_COLORMAGENTA,
  1745.                      NX_COLORORANGE, NX_COLORPURPLE, NX_COLORBROWN};
  1746. // Those colors come from /usr/include/appkit/color.h
  1747.  
  1748.   /* Adjust the lineMatrix, symbolMatrix, and legendForm */
  1749.   [self adjustPanels:oldncurves :pdh->ncurves];
  1750.  
  1751.   if ([columnPanel isVisible])
  1752.     [columnSelectionHandler fixPanel:self];
  1753.  
  1754.   /*
  1755.    * Error bars don't get drawn unless the error bar matrix is correct.
  1756.    * Therefore we don't test on visibility here, rather we test on pdh itself.
  1757.    */
  1758.   if (pdh->has_exbars || pdh->has_eybars)
  1759.     [errorBarHandler updatePanel:self];
  1760.  
  1761.   if ([fileRemovalPanel isVisible])
  1762.     [self fixFileRemovalPanel:self];
  1763.  
  1764.   /* reset plot button */
  1765.   [plotButton setAltTitle:"Plotting"];
  1766.   [plotButton highlight:NO];
  1767.  
  1768.   [self findMinMax:pdh];
  1769.   /*
  1770.    * Don't bother to check lin/log unless this is the first file or
  1771.    * we already have at least one logarithmic axis:
  1772.    */
  1773.   if (nfilestotal==1 || [self xaxisLog] || [self yaxisLog] ) {
  1774.     [self checkLinLog:pdh];
  1775.   }
  1776.   else {
  1777.     pdh->xaxislin = YES;
  1778.     pdh->yaxislin = YES;
  1779.   }
  1780.  
  1781.   curvecolors = (NXColor *)realloc((void *)curvecolors,
  1782.                               (ncurvestotal + pdh->ncurves) * sizeof(NXColor));
  1783.  
  1784.   [[canvas window] setTitleAsFilename:fname];
  1785.  
  1786.   if (!colorOption) {
  1787.     for (j=0; j<pdh->ncurves; j++) {
  1788.       curvecolors[j + ncurvestotal] = NX_COLORBLACK;
  1789.     }
  1790.   }
  1791.   else {            /* adjust all the curve colors */
  1792. //    for (j=0; j<pdh->ncurves + ncurvestotal; j++) {
  1793. //      hue = 0.6667 * (float)((j+1) % MIN(11, ncurvestotal + pdh->ncurves))
  1794. //                       / (float)MIN(10, ncurvestotal + pdh->ncurves);
  1795. //      curvecolors[j] = NXConvertHSBToColor(hue, 1.0, 1.0);
  1796. //      /* update curve no. on color panel */
  1797. //      [curveNumber setIntValue:j+1];
  1798. //      /* and update the color */
  1799. //      [curveColorWell setColor:curvecolors[j]];
  1800. //    }
  1801. // The preceding has the unfortunate effect of changing colors on an already-plotted
  1802. // curve if a new file is read in.  This is undesirable.  Here is a more
  1803. // primitive method.  Maybe a method based on (repeatable) random number
  1804. // generation would be appropriate.
  1805. //    for (j=0; j<pdh->ncurves; j++) {
  1806. //      hue = 0.6667 * (float)((j+ncurvestotal+1) % 6) / 5.0;
  1807. //      curvecolors[j + ncurvestotal] = NXConvertHSBToColor(hue, 1.0, 1.0);
  1808. //      /* update curve no. on color panel */
  1809. //      [curveNumber setIntValue:j+ncurvestotal+1];
  1810. //      /* and update the color */
  1811. //      [curveColorWell setColor:curvecolors[j+ncurvestotal]];
  1812. //    }
  1813. // All right, here is the method based on random numbers.  Maybe this will be OK.
  1814. // The trouble with it is that with srandom(10) [see above -- this was about
  1815. // the best] the first two curves are about the same shade of green.  Now the
  1816. // strategy is to preset the first few curve colors, after that they will be
  1817. // random.
  1818.     for (j=0; j<pdh->ncurves; j++) {
  1819.       if (j + ncurvestotal < N_PREDEFINED_COLORS) {
  1820.     curvecolors[j + ncurvestotal] = predefined_colors[j + ncurvestotal];
  1821.       }
  1822.       else {
  1823.     hue = 0.8 * ((float)random())/2147483647.0 ; /* 2^31 - 1 */
  1824.     curvecolors[j + ncurvestotal] = NXConvertHSBToColor(hue, 1.0, 1.0);
  1825.       }
  1826.       /* update curve no. on color panel */
  1827.       [curveNumber setIntValue:j+ncurvestotal+1];
  1828.       /* and update the color */
  1829.       [curveColorWell setColor:curvecolors[j+ncurvestotal]];
  1830.     }
  1831.   }
  1832.  
  1833.   return self;
  1834. }
  1835.  
  1836. - preludeToReading:(char *)fname :(datahunk **)pdh
  1837. {
  1838.   datahunk dh;
  1839.  
  1840.   /* set plot button title "Reading" */
  1841.   [plotButton setAltTitle:"Reading"];
  1842.   [plotButton highlight:YES];
  1843.   NXPing();            /* force plotButton redraw */
  1844.   
  1845.   if (nfilestotal == 0) {
  1846.     datahunkArray = [Storage newCount:1
  1847.       elementSize:sizeof(datahunk)
  1848.       description:"{*{float *}{float *}{float **}{float **}iiffff{BOOL}{BOOL}{BOOL}{BOOL}}"];
  1849.     *pdh = (datahunk *)[datahunkArray elementAt:0];
  1850.     if (*pdh == NULL) {
  1851.       NXRunAlertPanel("readData",
  1852.               "Weird error 0: NULL pointer in readData\n"
  1853.               "I can't continue",
  1854.               "OK", NULL, NULL);
  1855.       exit(0);
  1856.     }
  1857.     nfilestotal = 1;
  1858.   }
  1859.   else {
  1860.     [datahunkArray addElement:(void *)&dh];
  1861.     *pdh = (datahunk *)[datahunkArray elementAt:(unsigned)nfilestotal];
  1862.     if (*pdh == NULL) {
  1863.       NXRunAlertPanel("readData",
  1864.               "Weird error 1: NULL pointer in readData\n"
  1865.               "I can't continue",
  1866.               "OK", NULL, NULL);
  1867.       exit(0);
  1868.     }
  1869.     nfilestotal++;
  1870.   }
  1871.   (*pdh)->filename = (char *)malloc(strlen(fname) + 1);
  1872.   strncpy((*pdh)->filename, fname, strlen(fname) + 1);
  1873.  
  1874.   (*pdh)->has_exbars = NO;
  1875.   (*pdh)->has_eybars = NO;
  1876.   if (strncmp([errorBars title], "y only", 6) == 0) {
  1877.     (*pdh)->has_eybars = YES;
  1878.   }
  1879.   else if (strncmp([errorBars title], "x only", 6) == 0) {
  1880.     (*pdh)->has_exbars = YES;
  1881.   }
  1882.   else if (strncmp([errorBars title], "x and y", 7) == 0) {
  1883.     (*pdh)->has_exbars = YES;
  1884.     (*pdh)->has_eybars = YES;
  1885.   }
  1886.  
  1887.   return self;
  1888. }
  1889.  
  1890. - plotPrepAndDraw
  1891. {
  1892.   /* Check for linear or log on x and y axes */
  1893.   [self checkGlobalLinLog];
  1894.  
  1895.   [self findGlobalMinMax];
  1896.  
  1897.   /* Only check and reset min/max if this is the first file */
  1898.   if (nfilestotal == 1) {
  1899.     [self niceMinMaxInc];
  1900.   }
  1901.  
  1902.   [self drawPlot:self];
  1903.     
  1904.   return self;
  1905. }
  1906.  
  1907. - writeDataFiles:sender
  1908. {
  1909.   int i, j, n;
  1910.   datahunk *pdh;
  1911.   NXStream *outputStream;
  1912.   char filename[1024], paneltitle[256];
  1913.   id    savePanel = [[SavePanel new] setRequiredFileType:""];
  1914.  
  1915.   if (nfilestotal == 0) {
  1916.     NXRunAlertPanel("Write Data", "No data", "OK", NULL, NULL);
  1917.   }
  1918.   else {
  1919.     for (n=0; n<nfilestotal; n++) {
  1920.       pdh = (datahunk *)[datahunkArray elementAt:(unsigned)n];
  1921.       sprintf(paneltitle, "Save file %d (%s)", n+1,
  1922.           strrchr(pdh->filename,'/')==NULL ? pdh->filename
  1923.                                            : strrchr(pdh->filename,'/')+1);
  1924.       [savePanel setTitle:paneltitle];
  1925.       [savePanel setAccessoryView:writeDataAccButton];
  1926.       if ([savePanel runModal]) {
  1927.     strncpy(filename, [savePanel filename], 1024);
  1928.     if ((outputStream = NXOpenMemory(NULL, 0, NX_WRITEONLY)) == NULL) {
  1929.       NXRunAlertPanel("Write Data", "Cannot open memory for file %d",
  1930.               "OK", NULL, NULL, n);
  1931.       return self;
  1932.     }
  1933.     if ([writeDataAccButton state]) { /* ascii write */
  1934.       for (i=0; i<pdh->npoints; i++) {
  1935.         NXPrintf(outputStream, "%g", pdh->x[i]);
  1936.         if (pdh->has_exbars) {
  1937.           NXPrintf(outputStream, " %g", pdh->ex[i]);
  1938.         }
  1939.         for (j=0; j<pdh->ncurves; j++) {
  1940.           NXPrintf(outputStream, " %g", *(*(pdh->y+j)+i));
  1941.           if (pdh->has_eybars) {
  1942.         NXPrintf(outputStream, " %g", *(*(pdh->ey+j)+i));
  1943.           }
  1944.         }
  1945.         NXPrintf(outputStream, "\n");
  1946.       }
  1947.     }
  1948.     else {            /* binary write */
  1949.       NXWrite(outputStream, pdh->x, (pdh->npoints)*sizeof(NXCoord));
  1950.       if (pdh->has_exbars) {
  1951.         NXWrite(outputStream, pdh->ex, (pdh->npoints)*sizeof(NXCoord));
  1952.       }
  1953.       for (j=0; j<pdh->ncurves; j++) {
  1954.         NXWrite(outputStream, *(pdh->y+j), (pdh->npoints)*sizeof(NXCoord));
  1955.         if (pdh->has_eybars) {
  1956.           NXWrite(outputStream, *(pdh->ey+j), (pdh->npoints)*sizeof(NXCoord));
  1957.         }
  1958.       }
  1959.     }
  1960.     NXFlush(outputStream);
  1961.     NXSaveToFile(outputStream, filename);
  1962.     NXClose(outputStream);
  1963.       }
  1964.     }
  1965.   }
  1966.   return self;
  1967. }
  1968.     
  1969. - whyTheBeep:sender
  1970. {
  1971.   switch(beepError) {
  1972.   case 0:
  1973.     NXRunAlertPanel("The Beep Panel", "Press this button if the program beeps\n"
  1974.        "and you want to know why", "OK", NULL, NULL);
  1975.     break;
  1976.   case 1:
  1977.     NXRunAlertPanel("The Beep Happened Because:",
  1978.             "x-axis changed from log to linear", "OK", NULL, NULL);
  1979.     break;
  1980.   case 2:
  1981.     NXRunAlertPanel("The Beep Happened Because:",
  1982.             "y-axis changed from log to linear", "OK", NULL, NULL);
  1983.     break;
  1984.   case 3:
  1985.     NXRunAlertPanel("The Beep Happened Because:",
  1986.             "x-increment was negative\n (I changed it)", "OK", NULL, NULL);
  1987.     break;
  1988.   case 4:
  1989.     NXRunAlertPanel("The Beep Happened Because:",
  1990.             "xmax was less than xmin\n (I changed them)", "OK", NULL, NULL);
  1991.     break;
  1992.   case 5:
  1993.     NXRunAlertPanel("The Beep Happened Because:",
  1994.             "y-increment was negative\n (I changed it)", "OK", NULL, NULL);
  1995.     break;
  1996.   case 6:
  1997.     NXRunAlertPanel("The Beep Happened Because:",
  1998.             "ymax was less than ymin\n (I changed them)", "OK", NULL, NULL);
  1999.     break;
  2000.   case 7:
  2001.     NXRunAlertPanel("The Beep Happened Because:",
  2002.             "Too many tic marks would have been on the x-axis\n"
  2003.             " (I changed the min, max, and/or increment)",
  2004.             "OK", NULL, NULL);
  2005.     break;
  2006.   case 8:
  2007.     NXRunAlertPanel("The Beep Happened Because:",
  2008.             "Too many tic marks would have been on the y-axis\n"
  2009.             " (I changed the min, max, and/or increment)",
  2010.             "OK", NULL, NULL);
  2011.     break;
  2012.   case 9:
  2013.     NXRunAlertPanel("The Beep Happened Because:",
  2014.             "x-axis was logarithmic but some datum was negative\n"
  2015.             " (I changed x-axis to linear)", "OK", NULL, NULL);
  2016.     break;
  2017.   case 10:
  2018.     NXRunAlertPanel("The Beep Happened Because:",
  2019.             "y-axis was logarithmic but some datum was negative\n"
  2020.             " (I changed y-axis to linear)", "OK", NULL, NULL);
  2021.     break;
  2022.   case 11:
  2023.     NXRunAlertPanel("The Beep Happened Because:",
  2024.             "tried to set color of a non-existent curve", "OK", NULL, NULL);
  2025.     break;
  2026.   case 12:
  2027.     NXRunAlertPanel("The Beep Happened Because:",
  2028.             "an increment was too small for a log axis\n"
  2029.             " (I reset it)", "OK", NULL, NULL);
  2030.     break;
  2031.   }
  2032.   return self;
  2033. }
  2034.  
  2035. - (NXColor) provideBackgroundColor
  2036. {
  2037.   if ( ( ([printPreview state] == 1) || (NXDrawingStatus == NX_PRINTING))
  2038.       && ([accPrintColorButton state] == 0) ) {
  2039.     return NX_COLORWHITE;
  2040.   }
  2041.   else {
  2042.     return backgroundcolor;
  2043.   }
  2044. }
  2045.  
  2046. - (NXColor) provideTextColor
  2047. {
  2048.   if ( ( ([printPreview state] == 1) || (NXDrawingStatus == NX_PRINTING))
  2049.       && ([accPrintColorButton state] == 0) ) {
  2050.     return NX_COLORBLACK;
  2051.   }
  2052.   else {
  2053.     return textcolor;
  2054.   }
  2055. }
  2056.  
  2057. - (NXColor) provideCurveColor:(int)aCurve
  2058. {
  2059.   if ( ( ([printPreview state] == 1) || (NXDrawingStatus == NX_PRINTING))
  2060.       && ([accPrintColorButton state] == 0) ) {
  2061.     return NX_COLORBLACK;
  2062.   }
  2063.   else {
  2064.     return curvecolors[aCurve];
  2065.   }
  2066. }
  2067.  
  2068. - setBackgroundColor:sender
  2069. {
  2070.   backgroundcolor = [sender color];
  2071.   return self;
  2072. }
  2073.  
  2074. - forceBackgroundColor:(NXColor) aColor
  2075. {
  2076.   backgroundcolor = aColor;
  2077.   return self;
  2078. }
  2079.  
  2080. - forceTextColor:(NXColor) aColor
  2081. {
  2082.   textcolor = aColor;
  2083.   return self;
  2084. }
  2085.  
  2086. - forceCurveColor:(int)curvenum :(NXColor)aColor
  2087. {
  2088.   if (curvenum >= ncurvestotal) {
  2089.     NXBeep();
  2090.     beepError = 11;
  2091.   }
  2092.   else {
  2093.     curvecolors[curvenum] = aColor;
  2094.   }
  2095.   return self;
  2096. }
  2097.  
  2098.  
  2099. - setTextColor:sender
  2100. {
  2101.   textcolor = [sender color];
  2102.   return self;
  2103. }
  2104.  
  2105. - setCurveColor:sender
  2106. {
  2107.   int curvenum = [curveNumber intValue];
  2108.  
  2109.   if (curvenum > ncurvestotal || curvenum < 1) {
  2110.     NXBeep();
  2111.     beepError = 11;
  2112.     return self;
  2113.   }
  2114. /* Change the color in the color well right away: */
  2115.   [sender setColor:[sender color]];
  2116.   NXPing();            /* make sure it gets displayed */
  2117.   curvecolors[curvenum-1] = [sender color];
  2118. /* Try to be helpful and increment the curvenumber */
  2119.   curvenum = (curvenum == ncurvestotal? 1 : curvenum + 1);
  2120. /* Slight delay (1/2 second) so the well doesn't change instantly.  */
  2121.   usleep((unsigned)500000);
  2122.   [curveNumber setIntValue:curvenum];
  2123. /* Tell the sender (a color well) to go on the next color: */
  2124.   [sender setColor:curvecolors[curvenum - 1]];
  2125.   return self;
  2126. }
  2127.  
  2128. /* Update the curveColorWell if the curve number is changed */
  2129. - textDidEnd:textObject endChar:(unsigned short)whyEnd
  2130. {
  2131.   int curvenum = [curveNumber intValue];
  2132.   if (curvenum > ncurvestotal || curvenum < 1) {
  2133.     NXBeep();
  2134.     beepError = 11;
  2135.     return self;
  2136.   }
  2137.   [curveColorWell setColor:curvecolors[curvenum-1]];
  2138.   NXPing();            /* make sure it gets displayed */
  2139.   return self;
  2140. }
  2141.  
  2142. /* Update the global colorOption variable. */
  2143. - colorOn:(BOOL)onOff
  2144. {
  2145.   if (onOff) {
  2146.     colorOption = YES;
  2147.     backgroundcolor = NX_COLORBLACK;
  2148.     textcolor = NX_COLORWHITE;
  2149.   }
  2150.   else {
  2151.     colorOption = NO;
  2152.     backgroundcolor = NX_COLORWHITE;
  2153.     textcolor = NX_COLORBLACK;
  2154.   }
  2155.   [textColorWell setColor:textcolor];
  2156.   [backgroundColorWell setColor:backgroundcolor];
  2157.   [curveColorWell setColor:textcolor];
  2158.  
  2159.   return self;
  2160. }
  2161.  
  2162. - fixMatrixColumn:sender    /* The sender is a matrix */
  2163. {
  2164.   int row, col, i;
  2165.  
  2166.   row = [sender selectedRow];
  2167.   col = [sender selectedCol];
  2168.   if (sender == lineMatrix) {
  2169.     [self adjustLineStyleMatrix:col :row];
  2170.     // Now, instead of redisplaying the whole matrix, just redraw the
  2171.     // cells in the affected column.  It's much faster this way.
  2172.     for (i=0; i<N_LINE_STYLES; i++)
  2173.       [lineMatrix drawCellAt:i :col];
  2174.   }
  2175.   else if (sender == symbolMatrix) {
  2176.     [self adjustSymbolTypeMatrix:col :row];
  2177.     // Comment above applies here, too.
  2178.     for (i=0; i<N_SYMBOL_STYLES; i++)
  2179.       [symbolMatrix drawCellAt:i :col];
  2180.   }
  2181.   return self;
  2182. }
  2183.  
  2184. /* change the column which is to be taken as the x data */
  2185. - swapColumns:(int)prev_col :(int)col forFileNumber:(int)i
  2186. {
  2187.   datahunk *pdh;
  2188.   float    *tmp;
  2189.  
  2190.   if (col < 0 || prev_col < 0)    /* be very cautious */
  2191.     return self;
  2192.  
  2193.   if (col==0 && prev_col==0)    /* must avoid this special case */
  2194.     return self;
  2195.  
  2196.   pdh    = (datahunk *)[datahunkArray elementAt:(unsigned)i];
  2197.   if (prev_col == 0) {        /* was first column, generic x-data */
  2198. //  tmp    = pdh->x;        /* why doesn't this order work? */
  2199. //  pdh->x = *(pdh->y + col-1);
  2200. //  *(pdh->y + col-1) = tmp;
  2201.     tmp    = *(pdh->y + col-1);
  2202.     *(pdh->y + col-1) = pdh->x;
  2203.     pdh->x = tmp;
  2204.   }
  2205.   else if (col == 0) {        /* revert back to generic x-data */
  2206.     tmp    = *(pdh->y + prev_col - 1);
  2207.     *(pdh->y + prev_col - 1) = pdh->x;
  2208.     pdh->x = tmp;
  2209.   }
  2210.   else {            /* swapping y's only */
  2211. //    tmp    = *(pdh->y + prev_col - 1);
  2212. //    *(pdh->y + prev_col - 1) = *(pdh->y + col - 1);
  2213. //    *(pdh->y + col - 1) = tmp;
  2214.     tmp    = *(pdh->y + col - 1);
  2215.     *(pdh->y + col - 1)      = *(pdh->y + prev_col - 1);
  2216.     *(pdh->y + prev_col - 1) = pdh->x;
  2217.     pdh->x = tmp;
  2218.   }
  2219.  
  2220.   [self findMinMax:pdh];    /* reset these values here */
  2221.   [self findGlobalMinMax];
  2222.  
  2223.   return self;
  2224. }
  2225.  
  2226.  
  2227. /*
  2228.  * This function is called by the PlotView object during zooming.
  2229.  */
  2230. - stackOldMinMax:(float)xmin :(float)xmax :(float)ymin :(float)ymax
  2231. {
  2232.   oldMin = currentMin;        /* structure assignment */
  2233.   oldMax = currentMax;
  2234.   oldInc = currentInc;        /* have to deal with the increments, too */
  2235.   currentMin.x = xmin;
  2236.   currentMax.x = xmax;
  2237.   currentMin.y = ymin;
  2238.   currentMax.y = ymax;
  2239.   currentInc.x = [xInc floatValueAt:0];
  2240.   currentInc.y = [yInc floatValueAt:0];
  2241.  
  2242.   return self;
  2243. }
  2244.  
  2245. /*
  2246.  * This method is invoked by the "Previous View" button on the
  2247.  * control panel.
  2248.  */
  2249. - previousView:sender
  2250. {
  2251.   NXPoint tmp;
  2252.  
  2253.   [self resetXmin:(double)oldMin.x];
  2254.   [self resetXmax:(double)oldMax.x];
  2255.   [self resetXinc:(double)oldInc.x];
  2256.   [self resetYmin:(double)oldMin.y];
  2257.   [self resetYmax:(double)oldMax.y];
  2258.   [self resetYinc:(double)oldInc.y];
  2259.   tmp        = oldMin;
  2260.   oldMin     = currentMin;
  2261.   currentMin = tmp;
  2262.   tmp        = oldMax;
  2263.   oldMax     = currentMax;
  2264.   currentMax = tmp;
  2265.   tmp        = oldInc;
  2266.   oldInc     = currentInc;
  2267.   currentInc = tmp;
  2268.   [self drawPlotButton:1];    /* display "Plotting" */
  2269.   [canvas display];
  2270.   [self drawPlotButton:0];    /* display "Plot" */
  2271.  
  2272.   return self;
  2273. }
  2274.  
  2275. @end
  2276.